//+------------------------------------------------------------------+
//|                                                         1122.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

// 交易参数
input int      LookBackBars = 30;    // 需要回溯的K线数量
input int      StopLossPoints = 300;  // 止损点数
input int      TakeProfitPoints = 300;// 止盈点数
input int      MagicNumber = 12345;   // EA魔术号
input double   LotSize = 0.1;         // 交易手数
input int      PendingOrderThreshold = 300;// 挂单触发点数阈值

// 画线参数
input int      StartBarForLines = 30;     // 画线起始K线位置
input int      EndBarForLines = 60;       // 画线结束K线位置
input color    HighLineColor = clrRed;    // 最高价线颜色
input color    LowLineColor = clrGreen;   // 最低价线颜色
input int      LineWidth = 2;             // 线宽

datetime       lastBarTime;           // 记录上一次的K线时间
double         lastHighest = EMPTY_VALUE; // 记录上一次的最高价
double         lastLowest = EMPTY_VALUE;  // 记录上一次的最低价

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   lastBarTime = iTime(_Symbol, _Period, 0);  // 初始化最后K线时间
   DrawRangeLines(); // 初始化时画线
//---
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| 绘制价格区间线函数                                                |
//+------------------------------------------------------------------+
void DrawRangeLines()
{
   // 检查参数有效性
   if(StartBarForLines <= 0 || EndBarForLines <= 0 || EndBarForLines <= StartBarForLines)
   {
      Print("画线参数错误：起始/结束位置无效");
      return;
   }
   
   // 计算需要复制的K线数量
   int count = EndBarForLines - StartBarForLines + 1;
   if(count <= 0) return;
   
   // 获取历史数据
   double highs[], lows[];
   int copiedHigh = CopyHigh(_Symbol, _Period, StartBarForLines, count, highs);
   int copiedLow = CopyLow(_Symbol, _Period, StartBarForLines, count, lows);
   
   if(copiedHigh <= 0 || copiedLow <= 0)
   {
      Print("获取历史数据失败！错误代码=", GetLastError());
      return;
   }
   
   // 计算最高价和最低价
   double highest = highs[ArrayMaximum(highs, 0, copiedHigh)];
   double lowest = lows[ArrayMinimum(lows, 0, copiedLow)];
   
   // 如果价格未变化且线条已存在，则无需更新
   if(lastHighest != EMPTY_VALUE && lastLowest != EMPTY_VALUE &&
      MathAbs(highest - lastHighest) < _Point && 
      MathAbs(lowest - lastLowest) < _Point)
   {
      return;
   }
   
   // 更新记录的价格
   lastHighest = highest;
   lastLowest = lowest;
   
   // 删除旧线条
   ObjectDelete(0, "HighLine_"+IntegerToString(MagicNumber));
   ObjectDelete(0, "LowLine_"+IntegerToString(MagicNumber));
   
   // 创建新高价线
   if(!ObjectCreate(0, "HighLine_"+IntegerToString(MagicNumber), OBJ_HLINE, 0, 0, highest))
   {
      Print("创建最高价线失败！错误代码=", GetLastError());
   }
   else
   {
      ObjectSetInteger(0, "HighLine_"+IntegerToString(MagicNumber), OBJPROP_COLOR, HighLineColor);
      ObjectSetInteger(0, "HighLine_"+IntegerToString(MagicNumber), OBJPROP_WIDTH, LineWidth);
      ObjectSetInteger(0, "HighLine_"+IntegerToString(MagicNumber), OBJPROP_STYLE, STYLE_SOLID);
   }
   
   // 创建新低价线
   if(!ObjectCreate(0, "LowLine_"+IntegerToString(MagicNumber), OBJ_HLINE, 0, 0, lowest))
   {
      Print("创建最低价线失败！错误代码=", GetLastError());
   }
   else
   {
      ObjectSetInteger(0, "LowLine_"+IntegerToString(MagicNumber), OBJPROP_COLOR, LowLineColor);
      ObjectSetInteger(0, "LowLine_"+IntegerToString(MagicNumber), OBJPROP_WIDTH, LineWidth);
      ObjectSetInteger(0, "LowLine_"+IntegerToString(MagicNumber), OBJPROP_STYLE, STYLE_SOLID);
   }
}

//+------------------------------------------------------------------+
//| 获取指定范围内最高价和最低价                                       |
//| 参数: symbol - 交易品种, timeframe - 时间周期, count - 回溯数量    |
//| 输出: high - 最高价, low - 最低价                                 |
//| 返回: bool - 成功返回true，失败返回false                          |
//+------------------------------------------------------------------+
bool GetHighLow(string symbol, ENUM_TIMEFRAMES timeframe, int count, double &high, double &low)
{
   double   highs[], lows[];  // 存储价格数据的数组
   int      copied;           // 实际复制的数据数量

   // 复制最高价数据（从索引1开始，跳过当前未完成的K线）
   copied = CopyHigh(symbol, timeframe, 1, count, highs);
   if(copied < 1) 
   {
      Print("获取最高价数据失败！错误代码=", GetLastError());
      return false;
   }

   // 复制最低价数据
   copied = CopyLow(symbol, timeframe, 1, count, lows);
   if(copied < 1) 
   {
      Print("获取最低价数据失败！错误代码=", GetLastError());
      return false;
   }

   // 计算最高价和最低价
   high = highs[ArrayMaximum(highs, 0, copied)];
   low = lows[ArrayMinimum(lows, 0, copied)];
   
   return true;
}

//+------------------------------------------------------------------+
//| 检查是否存在指定类型的持仓                                         |
//+------------------------------------------------------------------+
bool ExistPosition(ENUM_POSITION_TYPE positionType)
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol && 
         PositionGetInteger(POSITION_MAGIC) == MagicNumber)
      {
         if(PositionGetInteger(POSITION_TYPE) == positionType)
            return true;
      }
   }
   return false;
}

//+------------------------------------------------------------------+
//| 检查是否存在指定类型的挂单                                         |
//+------------------------------------------------------------------+
bool ExistOrder(ENUM_ORDER_TYPE orderType)
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      ulong ticket = OrderGetTicket(i);
      if(ticket > 0 && OrderGetString(ORDER_SYMBOL) == _Symbol && 
         OrderGetInteger(ORDER_MAGIC) == MagicNumber)
      {
         if(OrderGetInteger(ORDER_TYPE) == orderType)
            return true;
      }
   }
   return false;
}

//+------------------------------------------------------------------+
//| 删除所有本EA在当前品种上的挂单                                     |
//+------------------------------------------------------------------+
void DeleteAllPendingOrders()
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      ulong ticket = OrderGetTicket(i);
      if(ticket > 0 && 
         OrderGetString(ORDER_SYMBOL) == _Symbol && 
         OrderGetInteger(ORDER_MAGIC) == MagicNumber)
      {
         MqlTradeRequest request;
         MqlTradeResult result;
         ZeroMemory(request);
         ZeroMemory(result);
         
         request.action = TRADE_ACTION_REMOVE;
         request.order = ticket;
         
         if(OrderSend(request, result))
            Print("已删除挂单 #", ticket);
         else
            Print("删除挂单失败 #", ticket, " 错误代码: ", GetLastError());
      }
   }
}

//+------------------------------------------------------------------+
//| 检查持仓并删除挂单（如果有持仓）                                   |
//| 返回: true - 检测到持仓并删除了挂单                                |
//|       false - 没有持仓                                            |
//+------------------------------------------------------------------+
bool CheckPositionAndDeleteOrders()
{
   // 检查当前品种是否有本EA的持仓
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0 && 
         PositionGetString(POSITION_SYMBOL) == _Symbol && 
         PositionGetInteger(POSITION_MAGIC) == MagicNumber)
      {
         Print("检测到持仓存在，正在删除所有挂单...");
         DeleteAllPendingOrders();
         return true; // 检测到持仓
      }
   }
   return false; // 没有持仓
}

//+------------------------------------------------------------------+
//| 发送突破挂单函数                                                 |
//+------------------------------------------------------------------+
void PlaceBreakoutOrders(double highest, double lowest)
{
   // 检查持仓并删除挂单（如果有持仓）
   if(CheckPositionAndDeleteOrders())
      return; // 退出函数，不再挂新单
   
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   ZeroMemory(result);
   
   // 计算止损止盈价格
   double entryBuy = highest;
   double slBuy = entryBuy - StopLossPoints * _Point;
   double tpBuy = entryBuy + TakeProfitPoints * _Point;
   
   double entrySell = lowest;
   double slSell = entrySell + StopLossPoints * _Point;
   double tpSell = entrySell - TakeProfitPoints * _Point;
   
   // 获取当前市场价格
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   
   // 检查并挂多单（买入止损）
   if(!ExistPosition(POSITION_TYPE_BUY) && !ExistOrder(ORDER_TYPE_BUY_STOP))
   {
      // 添加价格条件：当前卖价低于最高价至少阈值点数
      if(currentAsk <= highest - PendingOrderThreshold * _Point)
      {
         request.action = TRADE_ACTION_PENDING;
         request.symbol = _Symbol;
         request.volume = LotSize;
         request.type = ORDER_TYPE_BUY_STOP;
         request.price = entryBuy;
         request.sl = slBuy;
         request.tp = tpBuy;
         request.deviation = 5;
         request.magic = MagicNumber;
         
         if(OrderSend(request, result))
            Print("挂多单成功! 价格=", entryBuy, " 止损=", slBuy, " 止盈=", tpBuy);
         else
            Print("挂多单失败! 错误=", GetLastError());
      }
      else
      {
         Print("未满足多单挂单条件：当前价(", currentAsk, ")未低于最高价(", highest, ")至少", PendingOrderThreshold, "点");
      }
   }
   
   // 检查并挂空单（卖出止损）
   if(!ExistPosition(POSITION_TYPE_SELL) && !ExistOrder(ORDER_TYPE_SELL_STOP))
   {
      // 添加价格条件：当前买价高于最低价至少阈值点数
      if(currentBid >= lowest + PendingOrderThreshold * _Point)
      {
         request.action = TRADE_ACTION_PENDING;
         request.symbol = _Symbol;
         request.volume = LotSize;
         request.type = ORDER_TYPE_SELL_STOP;
         request.price = entrySell;
         request.sl = slSell;
         request.tp = tpSell;
         request.deviation = 5;
         request.magic = MagicNumber;
         
         if(OrderSend(request, result))
            Print("挂空单成功! 价格=", entrySell, " 止损=", slSell, " 止盈=", tpSell);
         else
            Print("挂空单失败! 错误=", GetLastError());
      }
      else
      {
         Print("未满足空单挂单条件：当前价(", currentBid, ")未高于最低价(", lowest, ")至少", PendingOrderThreshold, "点");
      }
   }
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   datetime currentBarTime = iTime(_Symbol, _Period, 0);
   
   // 检测新K线开始（当前K线结束时触发）
   if(currentBarTime != lastBarTime)
   {
      // 延迟打印确保在K线结束时输出
      if(lastBarTime != 0) 
      {
         double highest, lowest;
         
         // 获取历史K线的最高最低价
         if(GetHighLow(_Symbol, _Period, LookBackBars, highest, lowest))
         {
            Print("在[", _Symbol, " ", EnumToString(_Period), "]周期上");
            Print("计算最近", LookBackBars, "根已完成K线的价格区间");
            Print("最高价: ", DoubleToString(highest, _Digits));
            Print("最低价: ", DoubleToString(lowest, _Digits));
            Print("挂单触发阈值: ", PendingOrderThreshold, "点");
            Print("=============================================");
            
            // 调用突破挂单函数
            PlaceBreakoutOrders(highest, lowest);
         }
      }
      
      // 更新最后K线时间
      lastBarTime = currentBarTime;
      
      // 每次新K线时绘制价格区间线
      DrawRangeLines();
   }
}

//+------------------------------------------------------------------+
//| 程序去初始化函数                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // 删除绘制的线条
   ObjectDelete(0, "HighLine_"+IntegerToString(MagicNumber));
   ObjectDelete(0, "LowLine_"+IntegerToString(MagicNumber));
}
//+------------------------------------------------------------------+